package org.example.com.pattern.structure.proxy

/**
 * 结构型模式-代理模式
 *
 */

/**
 * 案例
 * 程序库提供了视频下载类，但是该类效率非常低。如果客户端多次请求同一个视频，程序会反复下载改视频，
 * 而不会将首次下载的文件缓存下来复用。
 *
 * 代理类实现和原下载器相同的接口，并将所有工作委派给原下载器。不过代理类会保存所有文件下载纪录，如果程序
 * 多次请求同一个文件，它会返回缓存文件。
 */

interface ThirdPartyTVLib {
    fun listVideos(): List<String>
    fun getVideoInfo(id: String): String
    fun downloadVideo(id: String)
}

class ThirdPartyTVClass : ThirdPartyTVLib {
    override fun listVideos(): List<String> {
        return emptyList()
    }

    override fun getVideoInfo(id: String): String {
        return "ThirdPartyTVClass getVideoInfo"
    }

    override fun downloadVideo(id: String) {
        println("ThirdPartyTVClass downloadVideo")
    }
}

/**
 * 代理类
 * 通过代理类实现缓存功能
 */
class CacheTVClass(
    private val service: ThirdPartyTVLib,
) : ThirdPartyTVLib {
    private val listCache = mutableListOf<String>()
    private val videoCache = mutableListOf<String>()
    var needReset: Boolean = false

    override fun listVideos(): List<String> {
        if (listCache.isEmpty() || needReset) {
            listCache.clear()
            listCache.addAll(service.listVideos())
        }
        return listCache
    }

    override fun getVideoInfo(id: String): String {
        if (videoCache.isEmpty() || needReset) {
            videoCache.clear()
            val videoInfo = service.getVideoInfo(id)
            videoCache.add(videoInfo)
        }
        return videoCache.find { it == id } ?: ""
    }

    override fun downloadVideo(id: String) {
        if (!downloadExists(id) || needReset) {
            service.downloadVideo(id)
        }
    }

    private fun downloadExists(id: String): Boolean {
        return false
    }
}

/**
 * 之前直接与服务对象交互的GUI类无需改变（前提是它仅通过接口与服务对象交互）.
 * 我们可以通过安全地传入一个代理对象代替真实的服务对象，因为它们都实现了相同的接口
 */
class TVManager(
    private val service: ThirdPartyTVLib,
) {
    private fun renderVideoPage(id: String) {
        service.getVideoInfo(id)
    }

    private fun renderListPanel() {
        service.listVideos()
    }

    fun reactOnUserInput() {
        renderVideoPage("")
        renderListPanel()
    }
}

fun main() {
    val tvClass = ThirdPartyTVClass()
    val cacheTVClass = CacheTVClass(tvClass)
    val tvManager = TVManager(cacheTVClass)
    tvManager.reactOnUserInput()
}